Skip to content

Conversation

Zeenobit
Copy link
Contributor

@Zeenobit Zeenobit commented Oct 5, 2025

This is related to #21384

Objective

The goal of this change is to enable EntityEvent to work with any Entity-like type which implements ContainsEntity.

Solution

  • Change return type of EntityEvent::event_target from Entity to &impl ContainsEntity
  • Change all appropriate call sites to use event_target().entity() instead of event_target()
  • Refactor EntityEvent::event_target_mut into EntityEvent::set_event_target

I'm not fully happy with this solution yet, but I'm opening the PR to discuss the options from here.

Mainly the issue revolves around set_event_target. To make this work, we need the underlying type to be constructible from an entity. This may not always be safe from user's perspective (it wouldn't be with Instance<T> for example).

Based on my understanding of the system, an event's target is only mutated in the case of event propagation, which makes sense.

To work around this, I propose that we flag EntityEvents as immutable or not; or "not propagatable" or not (I'm open to other terminology! :P).

This would allow us to implement set_event_target as unreachable()! and throw an error instead if the user tries to propagate such an event. We could even maybe enforce this compile time but it'll require additional complexity (mostly in form of different permutations of trigger_* method family).

Testing

  • Added test_derive_entity_event to cover all permutations

@Zeenobit Zeenobit marked this pull request as draft October 5, 2025 21:39
@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Oct 6, 2025
Copy link
Contributor

github-actions bot commented Oct 6, 2025

It looks like your PR is a breaking change, but you didn't provide a migration guide.

Please review the instructions for writing migration guides, then expand or revise the content in the migration guides directory to reflect your changes.

@alice-i-cecile alice-i-cecile added S-Needs-Design This issue requires design work to think about how it would best be accomplished and removed S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Oct 6, 2025
@alice-i-cecile
Copy link
Member

&impl ContainsEntity

I'm reluctant to do this as it will break object safety. That will be a pretty widespread problem when trying to fix this though.

@Zeenobit
Copy link
Contributor Author

Zeenobit commented Oct 6, 2025

I'm reluctant to do this as it will break object safety. That will be a pretty widespread problem when trying to fix this though.

It won't be needed with @cart 's suggestion. I'll revise the CL.

How do we feel about immutable entity events though?

@alice-i-cecile
Copy link
Member

How do we feel about immutable entity events though?

Generally open to it, but haven't thought about this hard enough to be convinced that they're the only solution :)

@Zeenobit
Copy link
Contributor Author

Zeenobit commented Oct 7, 2025

I've updated the PR with the simplified implementation. Great idea! :)

Generally open to it, but haven't thought about this hard enough to be convinced that they're the only solution :)

I'm open to suggestions!

From my usage of the engine so far, I have yet to find a use case for propagating events, which as I understand, is the only reason entity events are mutable.

So from my perspective, I could easily envision all entity events being immutable by default, and only mutable if #[entity_event(propagate)] is specified. Attempting to propagate an event not flagged with #[entity_event(propagate)] seems like a degenerate case to me, and so we could easily treat it as an error.

If we do that, we could then implement set_event_target with unreachable!() for immutable events, since it would never be called. Additionally, I would go as far as marking set_event_target as unsafe. In my mind, this function should never be called externally.

@cart
Copy link
Member

cart commented Oct 7, 2025

On the topic of immutable entity events, I'm on board for that, but I think we'd do that at the trait level and break it out as SetEntityEventTarget (where specifying propagate in the EntityEvent derive would provide that impl).

From my usage of the engine so far, I have yet to find a use case for propagating events, which as I understand, is the only reason entity events are mutable.

Making them mutable would also theoretically enable a bulk API (ex: commands.trigger_targets(MyEvent { entity: Entity::PLACEHOLDER }, [e1, e2, e3])). But that feels a bit dirty to me / I'm pretty fine with losing it.

This change adds a new `SetEntityEventTarget` to handle mutable entity events.
@Zeenobit
Copy link
Contributor Author

Zeenobit commented Oct 8, 2025

I've added SetEntityEventTarget as discussed. Much cleaner implementation than what I had in mind! :)

For me, the only remaining issue is the EntityCommand::trigger call.

Should I split that into a separate PR or should I continue working in this PR?

Any opinions on how to refactor trigger?

In my opinion, the current implementation goes against Rust nomenclature. Typically in Rust we have functions like verb which take some data as input, or verb_with which take a constructor Fn.
We only have trigger(f: impl FnOnce(Entity) -> E).

To fix this properly, I think trigger should just be trigger(e: E) and we add trigger_with(f: impl FnOnce(Entity) -> E); but that would be a breaking change (which this PR already is? Maybe? 😅). I'm also open to other ideas on the naming.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Needs-Design This issue requires design work to think about how it would best be accomplished
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants